<div class="canvas-container ">
  <div class="controls-container">
    <div>
      <div> Start pressure ({(startPressure/3.).toFixed(2)})</div>
      <div><input bind:value="startPressure" type="range" min=0 max=3></div>
    </div>
    <div>
      <div> End pressure ({(endPressure/3.).toFixed(2)})</div>
      <div><input bind:value="endPressure" type="range" min=0 max=3></div>
    </div>
    <div>
      <div> Brush size ({(brushSize/3.).toFixed(2)})</div>
      <div><input bind:value="brushSize" type="range" min=0 max=3></div>
    </div>
    <div>
      <input bind:value="color" type="color" id="head" name="head" value="#e66465">
    </div>
    <div>
        <button on:click="buttonWasPressed()">{button_text}</button>
    </div>
  </div>
  <img ref:image hidden src="images/mypaint-vis/strokes/output.jpg" alt="">
  <canvas on:click="handleClick(event)" class="stroke-canvas" ref:canvas id="stroke" width="256" height="256"></canvas>
</div>
  
  
  <style>
.controls-container {
  padding-right: 30px;
  font-size: 0.8em;
}

.canvas-container {
  height: 100%;
  display: flex;
  justify-content: center;
}

.stroke-canvas {
  border: 1px solid black;
}
  </style>
  
  
  <script>
  const SCALE = 4;
  const SELECT_RADIUS = 5;
  const START_X = 8;
  const START_Y = 8;
  const START_COLOR = 'black';
  const END_COLOR = 'green';
  const CONTROL_COLOR = 'orange';
  const END_LIST = [
    [56, 56],
    [56, 8],
    [8, 56]
  ]
  const CONTROL_LIST = [
    [32, 8],
    [56, 32],
    [32, 56]
  ]


  function preloadMapping() {
    const strings = [];
    for (let end of END_LIST.values()) {
      for (let control of CONTROL_LIST.values()) {
        for (let size of [0, 1, 2, 3].values()) {
          for (let startPressure of [0, 1, 2, 3].values()) {
            for (let endPressure of [0, 1, 2, 3].values()) {
              for (let randIdx of [0, 1, 2].values()) {
                const newString = [end[0], end[1], control[0], control[1], size, startPressure, endPressure, randIdx].join("_");
                strings.push(newString);
              }
            }
          }
        }
      }
    }
    strings.sort();

    const stringToIdx = {};
    strings.map((value, idx) => {
      stringToIdx[value] = idx;
    });
    return stringToIdx;
  }

  const STRING_TO_IDX = preloadMapping();

  function check_distance_within_limit(pointA, pointB, limit) {
    return limit > Math.sqrt((pointA[0]-pointB[0])**2 + (pointA[1]-pointB[1])**2);
  }

  function hexToRgb(hex) {
    // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
    var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
    hex = hex.replace(shorthandRegex, function(m, r, g, b) {
        return r + r + g + g + b + b;
    });

    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
    } : null;
  }

  export default {

    onstate({ changed, current, previous }) {
			// this fires before oncreate, and on every state change.
			// the first time it runs, `previous` is undefined
      if (previous !== undefined) {
        this.drawCanvas();
      }
		},

    oncreate() {
      const canvas = this.refs.canvas;
      this.ctx = canvas.getContext('2d');

      this.refs.image.onload = () => {
        this.drawCanvas();
        this.buttonWasPressed();
      }
    },

    //onupdate({ changed, current, previous }) {
			// this fires after oncreate, and after every state change
			// once the DOM is synchronised with the data
    //  console.log(`The DOM has been updated`, changed, current, previous);
    //  this.drawCanvas();
		//},
    
    methods: {
      buttonWasPressed() {
        const { timer, buttonState } = this.get();
        const newButtonState = (buttonState+1) % 2;
        clearInterval(timer);
        if (newButtonState === 0) {
          const newTimer = setInterval(() => this.tick(), 100);
          this.set({timer: newTimer});
        }
        this.set({buttonState: newButtonState});
      },

      tick() {
        const {randIdx} = this.get();
        this.set({randIdx: (randIdx + 1)%3});
      },

      handleClick(event) {
        const rect = this.refs.canvas.getBoundingClientRect();
        const x = (event.clientX - rect.left) / SCALE;
        const y = (event.clientY - rect.top) / SCALE;
        console.log("x: " + x + " y: " + y);
        for (let i = 0; i < 3; i++) {
          if (check_distance_within_limit([x, y], END_LIST[i], SELECT_RADIUS)) {
            this.set({endPoint: END_LIST[i]});
            return;
          }
          if (check_distance_within_limit([x, y], CONTROL_LIST[i], SELECT_RADIUS)) {
            this.set({controlPoint: CONTROL_LIST[i]});
            return;
          }
        }
      },

      drawSelectors() {
        const ctx = this.ctx;

        ctx.lineWidth = 2;
        ctx.setLineDash([4, 2]);

        ctx.fillStyle = START_COLOR;
        ctx.beginPath();
        ctx.arc(SCALE*START_X, SCALE*START_Y, SELECT_RADIUS, 0, Math.PI * 2, true);
        ctx.fill();

        ctx.lineWidth = 2;
        ctx.setLineDash([4, 2]);

        ctx.strokeStyle = CONTROL_COLOR;
        for (let i = 0; i < 3; i++) {
          ctx.beginPath();
          ctx.arc(SCALE*CONTROL_LIST[i][0], SCALE*CONTROL_LIST[i][1], SELECT_RADIUS, 0, Math.PI * 2, true);
          ctx.stroke();
        }

        ctx.strokeStyle = END_COLOR;
        for (let i = 0; i < 3; i++) {
          ctx.beginPath();
          ctx.arc(SCALE*END_LIST[i][0], SCALE*END_LIST[i][1], SELECT_RADIUS, 0, Math.PI * 2, true);
          ctx.stroke();
        }
      },

      drawShadedSelectors() {
        const { endPoint, controlPoint } = this.get();
        const ctx = this.ctx;

        ctx.fillStyle = CONTROL_COLOR;
        ctx.beginPath();
        ctx.arc(SCALE*controlPoint[0], SCALE*controlPoint[1], SELECT_RADIUS, 0, Math.PI * 2, true);
        ctx.fill();

        ctx.fillStyle = END_COLOR;
        ctx.beginPath();
        ctx.arc(SCALE*endPoint[0], SCALE*endPoint[1], SELECT_RADIUS, 0, Math.PI * 2, true);
        ctx.fill();
      },

      convertAndDrawImage() {
        const { color, sprite_index } = this.get();

        this.ctx.drawImage(this.refs.image, sprite_index[0]*64, sprite_index[1]*64, 64, 64, 0, 0, 256, 256);
        const imgdata = this.ctx.getImageData(0, 0, 256, 256);
        const rgba = imgdata.data;
        
        let darkestGray = 255;
        const arraylength = 256 * 256 * 4;
        //Common formula for converting to grayscale.
        //gray = 0.3*R + 0.59*G + 0.11*B
        for (let i=arraylength-1; i>0;i-=4) {
          //R= i-3, G = i-2 and B = i-1
          //Get our gray shade using the formula
          const gray = 0.3 * rgba[i-3] + 0.59 * rgba[i-2] + 0.11 * rgba[i-1];
          //Set our 3 RGB channels to the computed gray.
          rgba[i-3] = gray;
          rgba[i-2] = gray;
          rgba[i-1] = gray;

          if (gray < darkestGray) {
            darkestGray = gray;
          }
        }
        
        const c = hexToRgb(color);
        for (let i=arraylength-1; i>0;i-=4) {
          const scale = (255.-rgba[i-3]) / (255.-darkestGray);

          rgba[i-3] = 255-(scale * (255-c.r));
          rgba[i-2] = 255-(scale * (255-c.g));
          rgba[i-1] = 255-(scale * (255-c.b));
        }
        
        this.ctx.putImageData(imgdata, 0, 0);
      },

      drawCanvas() {
        this.clearCanvas();
        this.convertAndDrawImage();
        this.drawSelectors();
        this.drawShadedSelectors();
      },

      clearCanvas() {
        this.ctx.clearRect(0, 0, 256, 256); // clear canvas
      }
    },
    
    data() {
      return {
        endPoint: END_LIST[0],
        controlPoint: CONTROL_LIST[0],
        startPressure: 1,
        endPressure: 1,
        brushSize: 1,
        randIdx: 0,
        color: "#e66465",

        buttonState: 1,
      }
    },

    computed: {
      sprite_index: ({endPoint, controlPoint, brushSize, startPressure, endPressure, randIdx}) => {
        const newString = [
          endPoint[0], endPoint[1], 
          controlPoint[0], controlPoint[1],
          brushSize, startPressure, 
          endPressure, randIdx].join("_");
        const newIdx = STRING_TO_IDX[newString];
        return [newIdx % 43, Math.floor(newIdx / 43)];
      },
      button_text: ({buttonState}) => (buttonState === 1) ? "play" : "pause"
    },
  }
  </script>
  